#include "bondquotebrokerchart.h"
#include <widgets/chart/basechart_plot.h>
#include <widgets/chart/basechart_graph.h>
#include <core/comdefs.h>
#include <core/Utility.h>
#include <core/time/system_time.h>
#include <core/StyleMgr.h>
#include <widgets/STips.h>
#include <qbtools/tools.h>

const int NULL_VALUE = -1000;
const int QUOTES_NUMBER = 7;
#define QB_VALID_DISPLAY_PRICE 0.0001
#define QB_VALID_MIN_PRICE		-999
#define CHARTBONDQUOTE_MINSHOW	4.f
#define min(a,b)            (((a) < (b)) ? (a) : (b))
#define max(a,b)            (((a) > (b)) ? (a) : (b))

namespace qb
{
	BondQuoteBrokerChart::BondQuoteBrokerChart(QWidget* parent)
		: QWidget(parent)
		, m_nBtnSelect(1)
		, m_nPrcType(prc_Yeild)
		, m_nMarketType(0)
		, m_nHotPoint(-1)
		, m_nRealBegin(0)
		, m_nRealEnd(0)
		, m_dMinX(0.0)
		, m_dMaxX(0.0)
		, m_dMinY(0.0)
		, m_dMaxY(0.0)
		, m_dOriginMinX(0.0)
		, m_dOriginMaxX(0.0)
		, m_dDragLeft(0.0)
		, m_dDragRight(0.0)
		, m_bCheckFilter(true)
		, m_bShowLine1(true)
		, m_bShowLine2(true)
	{
	}

	BondQuoteBrokerChart::~BondQuoteBrokerChart()
	{
	}

	void BondQuoteBrokerChart::setBondInfo(const CBondInfo* bondinfo)
	{
		mBondInfo = (CBondInfo*)bondinfo;
	}

	int BondQuoteBrokerChart::getMarketType() {
		return m_nMarketType;
	}

	void BondQuoteBrokerChart::loadKLineData(xQBABrokerKlineDayList_c* pData)
	{
		inputKLineData(pData);
		refreshData();
	}

	void BondQuoteBrokerChart::loadRealTimeData(xQBMarketStreamList_c* pData)
	{
		inputRealTimeData(pData);
		refreshData();
	}

	void BondQuoteBrokerChart::createChart()
	{
		verticalLayout = new QVBoxLayout(this);
		customPlot = new BaseChartCustomPlot(this);
		verticalLayout->addWidget(customPlot);
		this->setLayout(verticalLayout);

		connect(customPlot, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(onMouseMove(QMouseEvent*)));
		connect(customPlot, &BaseChartCustomPlot::mouseDoubleClick, [=]() { emit mouseDoubleClick(this); });

		QBrush boxBrush(StyleMgr::instance().color("BasicPlate"));
		customPlot->setBackground(boxBrush);
		customPlot->addGraph(customPlot->xAxis, customPlot->yAxis);
		customPlot->xAxis->grid()->setCustomAxisGrid(this);
		customPlot->yAxis->grid()->setCustomAxisGrid(this);
		customPlot->xAxis->setYAxis(customPlot->yAxis);
		customPlot->yAxis->setTickLabelPadding(40);
		customPlot->graph(0)->setPen(QPen(QColor(205, 119, 4)));
		customPlot->graph(0)->setLineStyle(BaseChartGraph::lsLine);
	//	customPlot->graph(0)->setScatterStyle(BaseChartScatterStyle(BaseChartScatterStyle::ssCircle, 3));

		if (m_nBtnSelect == 1)
		{
			customPlot->addGraph(customPlot->xAxis, customPlot->yAxis);
			customPlot->graph(1)->setPen(QPen(QColor(195, 29, 22)));
			customPlot->graph(1)->setLineStyle(BaseChartGraph::lsLine);
		//	customPlot->graph(1)->setScatterStyle(BaseChartScatterStyle(BaseChartScatterStyle::ssCircle, 3));
		}

		customPlot->xAxis->setNumberPrecision(4);
		customPlot->xAxis->setCustomGrid();
		customPlot->yAxis->setCustomGrid();
		customPlot->xAxis->setSubTicks(false);
		customPlot->yAxis->setSubTicks(false);
		customPlot->yAxis->ticker()->setTickCount(5);

		QString qText1 = "Bid";
		QString qText2 = "Ofr";
		BaseChartTextElement* pte = new BaseChartTextElement(customPlot, qText1, qText2, QFont("Microsoft YaHei", 9, QFont::Bold));
		pte->setCustomDraw(this);
		pte->setTextFlags(Qt::AlignLeft | Qt::AlignVCenter);
		pte->setTextOffset(18);
		pte->setTextColor(StyleMgr::instance().color("TextNormal"));
		customPlot->plotLayout()->addElement(1, 0, pte);

		bcde = new BaseChartDragBarElement(customPlot);
		customPlot->plotLayout()->addElement(2, 0, bcde);

		refreshData();
	}

	void BondQuoteBrokerChart::setSelect(int select)
	{
		m_nBtnSelect = select;
	}

	void BondQuoteBrokerChart::setAbCheck(int check)
	{
		m_bCheckFilter = (check == 1 ? true : false);
		refreshData();
	}

	void BondQuoteBrokerChart::setPriceType(emPrice prcType)
	{
		int nSet = (int)prcType;
		if (nSet == m_nPrcType) return;
		m_nPrcType = nSet;
	}

	void BondQuoteBrokerChart::setMarketType(em_BrokerKinds emCurType)
	{
		if (emCurType == em_Exchange)
			m_nMarketType = 2;
		else if (emCurType == em_CFETS)
			m_nMarketType = 1;
		else
			m_nMarketType = 0;
	}

	void BondQuoteBrokerChart::setLeftDrag(double ratio)
	{
		QB_CHECK_RETURN_VOID1(customPlot);

		if (m_nBtnSelect == 1)
		{
			PriceRealTimeList* pRealTime = GetPriceRealTimeList();
			if (!pRealTime || pRealTime->m_List.size() <= 0) {
				return;
			}

			double dBegin = ratio * (m_dOriginMaxX - m_dOriginMinX);
			int nBegin = (int)dBegin;
			m_dDragLeft = max(dBegin, m_dOriginMinX + 0.1);
			m_nRealBegin = max(nBegin, m_dOriginMinX);
		}
	}

	void BondQuoteBrokerChart::setRightDrag(double ratio)
	{
		QB_CHECK_RETURN_VOID1(customPlot);

		if (m_nBtnSelect == 1)
		{
			PriceRealTimeList* pRealTime = GetPriceRealTimeList();
			if (!pRealTime || pRealTime->m_List.size() <= 0) {
				return;
			}

			double dEnd = ratio * (m_dOriginMaxX - m_dOriginMinX);
			int nEnd = (int)dEnd;
			m_dDragRight = min(dEnd, m_dOriginMaxX);
			m_nRealEnd = min(nEnd + 1, m_dOriginMaxX);

			if (ratio >= 1.0)
			{
				m_nRealEnd = 0;
			}
		}
	}

	void BondQuoteBrokerChart::setCentralDrag(double ratio1, double ratio2)
	{
		setLeftDrag(ratio1);
		setRightDrag(ratio2);
	}

	PriceKLineList* BondQuoteBrokerChart::GetPriceKLineList()
	{
		PriceKLineList* pList = &m_DataKLineBroker;
		if (m_nMarketType == 1)pList = &m_DataKLineCFETS;
		else if (m_nMarketType == 2)pList = &m_DataKLineExchange;
		return pList;
	}

	PriceRealTimeList* BondQuoteBrokerChart::GetPriceRealTimeList() {
		PriceRealTimeList* pQuotes = GetPriceRealTimeList2();
		const std::list<PriceRealTime>& list = pQuotes->m_List;
		if (NeedToFilter() && list.size() > QUOTES_NUMBER) {
			pQuotes = &m_quotesFiltered;
		}

		return pQuotes;
	}

	PriceRealTimeList* BondQuoteBrokerChart::GetPriceRealTimeList2() {
		PriceRealTimeList* pList = &m_DataRealTimeBroker;
		if (m_nMarketType == 1)pList = &m_DataRealTimeCFETS;
		else if (m_nMarketType == 2)pList = &m_DataRealTimeExchange;
		return pList;
	}

	bool BondQuoteBrokerChart::drawGridXLines(BaseChartPainter* painter)
	{
		if (m_nBtnSelect == 0)drawGridXLines1(painter);
		else if (m_nBtnSelect == 1)drawGridXLines2(painter);

		return true;
	}

	bool BondQuoteBrokerChart::drawGridYLines(BaseChartPainter* painter)
	{
		QString szValue;
		QRectF rfText;
		int nTextTop = 10000;//纵轴刻度文字
		int top = customPlot->yAxis->axisRect()->top();
		int bottom = customPlot->yAxis->axisRect()->bottom();
		int tickCount = (customPlot->yAxis->ticker()->tickCount() == 0 ? 5 : customPlot->yAxis->ticker()->tickCount());
		double step = (m_dMaxY - m_dMinY) / tickCount;
		QRectF rect(0, 0, 10000, 10000);
		int nFlag = Qt::TextDontClip | Qt::AlignLeft | Qt::AlignVCenter;
		int i = 0;

		bool isPriceDuplicated = [&](void)->bool {
			if (BondChartTools::GetAccuracyValue() != 2) {
				return false;
			}
			int ii = 0;
			float lastPrice = 0.0f;
			while (1) {
				float price = m_dMinY + step * ii;
				int Y = customPlot->yAxis->coordToPixel(price);
				if (Y < top || Y > bottom)break;
				if (ii > 0 && fabs(price - lastPrice) < 0.01) {
					return true;
				}
				lastPrice = price;
				ii++;
			}
			return false;
		}();

		while (1) {
			float price = m_dMinY + step * i;
			int Y = customPlot->yAxis->coordToPixel(price);
			if (Y < top - 1 || Y > bottom) break;
			if (Y <= bottom) 
			{
				painter->drawLine(QLineF(customPlot->xAxis->axisRect()->left() - customPlot->yAxis->tickLabelPadding(), Y, customPlot->xAxis->axisRect()->right(), Y));
				

				if (BondChartTools::GetAccuracyValue() == 4 || //设置4位精度
					((BondChartTools::GetAccuracyValue() == 2) && isPriceDuplicated)) {//设置2位精度，但有重复数据
					szValue = QString("%1").arg(price, 0, 'f', 4, '0');
				}
				else {
					szValue = QString("%1").arg(price, 0, 'f', 2, '0');
				}

				if (szValue.length() > 0)
				{
					QRectF measureRect = painter->boundingRect(rect, nFlag, szValue);
					if (nTextTop > Y + measureRect.height()) {
						QPen oldPen = painter->pen();
						painter->setPen(QColor(128, 128, 128, 255));
						painter->drawText(customPlot->xAxis->axisRect()->left() - customPlot->yAxis->tickLabelPadding() + 2, Y - measureRect.height(), measureRect.width(), measureRect.height(), nFlag, szValue);
						painter->setPen(oldPen);
						nTextTop = Y - measureRect.height();
					}
				}
			}
			i++;
			if (i > 100)
				break;
		}

		painter->drawLine(QLineF(customPlot->xAxis->axisRect()->left() - customPlot->yAxis->tickLabelPadding(), top, customPlot->xAxis->axisRect()->right(), top));

		return true;
	}

	void BondQuoteBrokerChart::drawGridXLines1(BaseChartPainter* painter)
	{
		PriceKLineList* pDataKLine = GetPriceKLineList();

		int count = 0;
		int firstindex = -1, lastindex = -1;
		for (int i = 0; i <= m_dMaxX; i++) {
			if (i >= m_dMinX && i <= m_dMaxX) {
				if (firstindex < 0)firstindex = i;
				lastindex = i;
				count++;
			}
		}
		if (firstindex < 0 || count <= 0)return;
		int dx = max(1, (int)(((float)count) / CHARTBONDQUOTE_MINSHOW + 0.4));
		int top = customPlot->yAxis->axisRect()->top();
		int bottom = customPlot->yAxis->axisRect()->bottom();

		int nFlag = Qt::TextDontClip | Qt::AlignCenter | Qt::AlignVCenter;
		QRectF rect(0, 0, 10000, 10000);

		std::list<PriceKLine>::iterator it;
		int n = GetKLineIndex(lastindex, it);
		if (n < 0)return;
		int X, LX;
		QString szText;
		QFont font("Microsoft YaHei");
		font.setPixelSize(12);
		painter->setFont(font);

		if (n == lastindex)
		{
			szText = GetKLineDateString(it);
			X = customPlot->xAxis->coordToPixel(lastindex);
			painter->drawLine(QLineF(X, bottom, X, top));

			QRectF measureRect = painter->boundingRect(rect, nFlag, szText);
			QPen oldPen = painter->pen();
			painter->setPen(QColor(128, 128, 128, 255));
			LX = min(customPlot->xAxis->axisRect()->right() - measureRect.width(), X - measureRect.width() / 2);
			painter->drawText(LX, bottom, measureRect.width(), measureRect.height(), nFlag, szText);
			painter->setPen(oldPen);
		}
		else
		{
			LX = customPlot->xAxis->axisRect()->right();
		}
		int nXEnd = -1;
		for (n = firstindex; n < lastindex; n += dx)
		{
			if (GetKLineIndex(n, it) != n)continue;
			szText = GetKLineDateString(it);
			X = customPlot->xAxis->coordToPixel(n);

			QRectF measureRect = painter->boundingRect(rect, nFlag, szText);
			if ((X + measureRect.width() / 2) >= LX)continue;
			if (X < nXEnd) continue;
			nXEnd = X + measureRect.width();
			painter->drawLine(QLineF(X, bottom, X, top));
			int i = 0;

			QPen oldPen = painter->pen();
			painter->setPen(QColor(128, 128, 128, 255));

			for (it = pDataKLine->m_List.begin(); it != pDataKLine->m_List.end(); it++, i++) {
				if (i == n) {
					szText = strInt2DateInTable(it->m_Date);
					QRectF measureRect = painter->boundingRect(rect, nFlag, szText);
					if (n == lastindex)
						if (X + measureRect.width() / 2 > customPlot->xAxis->axisRect()->right())
							painter->drawText(customPlot->xAxis->axisRect()->right() - measureRect.width(), bottom, measureRect.width(), measureRect.height(), nFlag, szText);
						else
							painter->drawText(X - measureRect.width() / 2, bottom, measureRect.width(), measureRect.height(), nFlag, szText);
					else
						painter->drawText(X - measureRect.width() / 2, bottom, measureRect.width(), measureRect.height(), nFlag, szText);

					break;
				}
			}

			painter->setPen(oldPen);
		}
	}

	void BondQuoteBrokerChart::drawGridXLines2(BaseChartPainter* painter)
	{
		drawMouseMoveLine(painter);

		int count = 0;
		int firstindex = -1, lastindex = -1;
		for (int i = 0; i <= m_dMaxX; i++) {
			if (i >= m_dMinX && i <= m_dMaxX) {
				if (firstindex < 0)firstindex = i;
				lastindex = i;
				count++;
			}
		}
		if (firstindex < 0 || count <= 0)return;
		int dx = max(1, (int)(((float)count) / CHARTBONDQUOTE_MINSHOW + 0.4));
		int top = customPlot->yAxis->axisRect()->top();
		int bottom = customPlot->yAxis->axisRect()->bottom();

		int nFlag = Qt::TextDontClip | Qt::AlignCenter | Qt::AlignVCenter;
		QRectF rect(0, 0, 10000, 10000);
		QRectF rfText;

		std::list<PriceRealTime>::iterator it;
		int X, LX;
		QString szText;
		int n = GetPriceRealTimeIndex(lastindex, it);
		if (n < 0)return;
		if (n == lastindex) {
			szText = GetPriceRealTimeString(it);
			X = customPlot->xAxis->coordToPixel(lastindex);
			painter->drawLine(QLineF(X, bottom, X, top));

			QRectF measureRect = painter->boundingRect(rect, nFlag, szText);
			QPen oldPen = painter->pen();
			painter->setPen(QColor(128, 128, 128, 255));
			LX = customPlot->xAxis->axisRect()->right() - measureRect.width();
			painter->drawText(LX, bottom, measureRect.width(), measureRect.height(), nFlag, szText);
			painter->setPen(oldPen);
		}
		else {
			LX = customPlot->xAxis->axisRect()->right();
		}

		int nColLoop = 0;
		for (n = firstindex; n < lastindex; n += dx) {
			if (GetPriceRealTimeIndex(n, it) != n)continue;
			szText = GetPriceRealTimeString(it);
			X = customPlot->xAxis->coordToPixel(n);
			QRectF measureRect = painter->boundingRect(rect, nFlag, szText);
			if ((X + measureRect.width() / 2) >= LX)continue;

			painter->drawLine(QLineF(X, bottom, X, top));

			QPen oldPen = painter->pen();
			painter->setPen(QColor(128, 128, 128, 255));
			painter->drawText(X - measureRect.width() / 2, bottom, measureRect.width(), measureRect.height(), nFlag, szText);
			painter->setPen(oldPen);
		}
	}

	bool BondQuoteBrokerChart::customDraw(BaseChartPainter* painter, const QRect& rect)
	{
		QRect rectBK = rect;
		rectBK.setRight(rectBK.left() + rect.width() / 2 - 2);
		painter->fillRect(rectBK, QColor(38, 55, 53));

		rectBK.setLeft(rectBK.left() + rect.width() / 2 + 2);
		rectBK.setRight(rect.right());
		painter->fillRect(rectBK, QColor(38, 55, 53));

		painter->setPen(QColor(205, 119, 4));
		painter->drawLine(QLineF(rect.left() + 6, rect.top() + rect.height() / 2, rect.left() + 14, rect.top() + rect.height() / 2));
		painter->drawLine(QLineF(rect.left() + 6, rect.top() + rect.height() / 2 + 1, rect.left() + 14, rect.top() + rect.height() / 2 + 1));

		painter->setPen(QColor(195, 29, 22));
		painter->drawLine(QLineF(rect.left() + rect.width() / 2 + 6, rect.top() + rect.height() / 2, rect.left() + rect.width() / 2 + 14, rect.top() + rect.height() / 2));
		painter->drawLine(QLineF(rect.left() + rect.width() / 2 + 6, rect.top() + rect.height() / 2 + 1, rect.left() + rect.width() / 2 + 14, rect.top() + rect.height() / 2 + 1));

		return true;
	}

	void BondQuoteBrokerChart::drawMouseMoveLine(BaseChartPainter* painter)
	{
		QB_CHECK_RETURN_VOID1(customPlot);
		if (m_nBtnSelect == 0)
		{
			return;
		}

		int x = mapFromGlobal(QCursor().pos()).x() - 8;
		int y = mapFromGlobal(QCursor().pos()).y() - 8;
		int top = customPlot->yAxis->axisRect()->top();
		int bottom = customPlot->yAxis->axisRect()->bottom();
		int left = customPlot->yAxis->axisRect()->left();
		int right = customPlot->yAxis->axisRect()->right();

		PriceRealTimeList* pRealTime = GetPriceRealTimeList();
		if (pRealTime && pRealTime->m_List.size() > 1)
		{
			right = min((int)customPlot->xAxis->coordToPixel(min((int)m_dMaxX, (int)(pRealTime->m_List.size() - 1))), right);
		}
		else
		{
			right = left;
		}

		if (x < left || x > right || y < top || y > bottom)
		{
			return;
		}

		painter->drawLine(QLineF(x, bottom, x, top));
	}

	void BondQuoteBrokerChart::inputKLineData(xQBABrokerKlineDayList_c* pData)
	{
		QB_CHECK_RETURN_VOID1(mBondInfo);

		xQBABrokerKlineDayList_c* pKLine = (xQBABrokerKlineDayList_c*)pData;
		if (!pKLine) 
		{
			m_DataKLineBroker.Clear();
			m_DataKLineCFETS.Clear();
			m_DataKLineExchange.Clear();

			return;
		}

		PriceKLineList* pKLineList = NULL;
		int nMarket = -1;
		if (strcmp(pKLine->m_markettype, "b") == 0) { pKLineList = &m_DataKLineBroker; nMarket = 0; }
		else if (strcmp(pKLine->m_markettype, "50") == 0) { pKLineList = &m_DataKLineCFETS; nMarket = 1; }
		else if (strcmp(pKLine->m_markettype, "e") == 0) { pKLineList = &m_DataKLineExchange; nMarket = 2; }
		if (NULL == pKLineList)return;

		pKLineList->Clear();

		if (pKLine->m_List.size() <= 0) 
		{
			return;
		}

		const char* pkey = mBondInfo->GetBondKey();
		const char* plm = mBondInfo->GetListedMarket();

		std::list<xQBABrokerKlineDayUnit_c>::iterator it;
		std::list<PriceKLine>::iterator it2;
		PriceKLine item;
		bool found = false;
		for (it = pKLine->m_List.begin(); it != pKLine->m_List.end(); ++it) 
		{
			if (strcmp(pkey, it->m_bond_key) == 0 && strcmp(plm, it->m_listed_market) == 0) {
				found = false;
				for (it2 = pKLineList->m_List.begin(); !found && it2 != pKLineList->m_List.end(); ++it2) {
					if (it2->m_Date == it->m_Date) {
						found = true;
						if (it->m_Symbol == 1) {
							it2->m_Price1 = it->m_Yield;
						}
						else if (it->m_Symbol == -1) {
							it2->m_Price2 = it->m_Yield;
						}
					}
				}
				if (!found) {
					item.m_Date = it->m_Date;
					if (it->m_Symbol == 1) {
						item.m_Price1 = it->m_Yield;
						item.m_Price2 = -1000;
					}
					else if (it->m_Symbol == -1) {
						item.m_Price1 = -1000;
						item.m_Price2 = it->m_Yield;
					}
					pKLineList->Insert(item);
				}
			}
		}
		pKLineList->m_List.sort();
	}

	void BondQuoteBrokerChart::inputRealTimeData(xQBMarketStreamList_c* pData)
	{
		QB_CHECK_RETURN_VOID1(mBondInfo && bcde);

		xQBMarketStreamList_c* pRealTime = pData;
		if ((!pRealTime || pRealTime->m_List.size() <= 0) && m_nBtnSelect != 1)return;
		std::list<xQBMarketStreamUnit_c>::iterator it = pRealTime->m_List.begin();

		GetPriceRealTimeList2()->Clear();
	
		const char* pkey = mBondInfo->GetBondKey();
		const char* plm = mBondInfo->GetListedMarket();
		bool bConvertible = (strcmp(mBondInfo->GetBondSubType(), "CVB") == 0 || strcmp(mBondInfo->GetBondSubType(), "SCV") == 0);
		CheckSelectMode(pRealTime);

		PriceRealTime item;
		PriceRealTimeList* listCurrent = GetPriceRealTimeList2();
		for (; it != pRealTime->m_List.end(); ++it) {
			if (strcmp(it->m_type, "1") == 0 && strcmp(pkey, it->m_body1.m_bondkey) == 0 && strcmp(plm, it->m_body1.m_listedmarket) == 0) {
				PriceRealTimeList* pList = listCurrent;
				xQBMarketStreamType1Body_c* pBody = &(it->m_body1);
				bool valid = false;
				item.Clear();
				item.m_time = it->m_modify_time;
				valid = item.SetPrice(it->m_modify_time, pBody->m_bid_yield, pBody->m_ofr_yield, pBody->m_bid_cleanprice,
					pBody->m_ofr_cleanprice, m_nPrcType == (int)prc_Yeild, bConvertible && strcmp(it->m_company_id, "e") == 0);

				if (valid)pList->Insert(item);
			}
		}
		listCurrent->m_List.sort();

		if (listCurrent->m_List.size() < 6)
		{
			bcde->setEnable(false);
		}
		else
		{
			bcde->setEnable(true);
		}

		m_dOriginMaxX = max(m_dOriginMaxX, listCurrent->m_List.size() - 1);
		m_dOriginMaxX = min(m_dOriginMaxX, listCurrent->m_List.size() - 1);

		if (m_nRealBegin > 0 && m_nRealBegin < (int)listCurrent->m_List.size() - 1)
		{
			auto itBegin = listCurrent->m_List.begin();
			auto itEnd = listCurrent->m_List.begin();
			std::advance(itEnd, m_nRealBegin);
			listCurrent->m_List.erase(itBegin, itEnd);
		}
		if (m_nRealEnd - m_nRealBegin > 0 && m_nRealEnd - m_nRealBegin < (int)listCurrent->m_List.size() - 1)
		{
			auto itBegin = listCurrent->m_List.begin();
			auto itEnd = listCurrent->m_List.end();
			std::advance(itBegin, m_nRealEnd - m_nRealBegin);
			listCurrent->m_List.erase(itBegin, itEnd);
		}
	}

	void BondQuoteBrokerChart::refreshData()
	{
		QB_CHECK_RETURN_VOID1(customPlot && customPlot->graph(0));

		FilterAbnormalVal();

		PriceKLineList* pDataKLine = GetPriceKLineList();
		PriceRealTimeList* pRealTime = GetPriceRealTimeList();

		m_dMinX = 0;
		m_dMaxX = CHARTBONDQUOTE_MINSHOW;
		if (m_nBtnSelect == 0 && pDataKLine->m_List.size() > 0)m_dMaxX = max(m_dMaxX, pDataKLine->m_List.size() - 1);
		else if (m_nBtnSelect == 1 && pRealTime->m_List.size() > 0)m_dMaxX = max(m_dMaxX, pRealTime->m_List.size() - 1);

		QVector<double>	vctX, vctY, vctX2, vctY2;

		if (1 == m_nBtnSelect)
		{
			int i = 0, j = 0;
			std::list<PriceRealTime>::iterator it;
			for (it = pRealTime->m_List.begin(); it != pRealTime->m_List.end(); ++it, i++, j++)
			{
				if ((*it).m_Price1 > 0)
				{
					vctX.push_back(i);
					vctY.push_back((*it).m_Price1);
				}

				if ((*it).m_Price2 > 0)
				{
					vctX2.push_back(j);
					vctY2.push_back((*it).m_Price2);
				}
			}

			customPlot->graph(0)->setData(vctX, vctY);
			customPlot->graph(1)->setData(vctX2, vctY2);
		}
		else
		{
			int i = 0;
			std::list<PriceKLine>::iterator it;
			for (it = pDataKLine->m_List.begin(); it != pDataKLine->m_List.end(); ++it, i++)
			{
				vctX.push_back(i);
				vctY.push_back((*it).m_Price1);
			}

			customPlot->graph(0)->setData(vctX, vctY);
		}
		
		GetShowLimit(m_dMinX, m_dMaxX, m_dMinY, m_dMaxY);
		double dExt = (m_dMaxX - m_dMinX) / 20;
		customPlot->yAxis->setRange(m_dMinY, m_dMaxY);
		customPlot->xAxis->setRange(m_dMinX, m_dMaxX+ dExt);

		if (m_nBtnSelect == 1 && pRealTime->m_List.size() > 0 && m_dDragLeft == 0.0 && m_dDragRight == 0.0)
		{
			int end = m_nRealEnd;
			if (m_nRealEnd == 0)
			{
				end = m_dOriginMaxX;
			}

			bcde->setPos(getXPos_Value(m_nRealBegin), getXPos_Value(end));
		}

		customPlot->replot(BaseChartCustomPlot::rpQueuedReplot);
	}

	void BondQuoteBrokerChart::GetShowLimit(double fMinXShow, double fMaxXShow, double& fMinYShow, double& fMaxYShow) 
	{
		if (0 == m_nBtnSelect)
		{
			GetShowLimit1(fMinXShow, fMaxXShow);
		}
		else
		{
			GetShowLimit2(fMinXShow, fMaxXShow);
		}

		if (fabs(fMaxYShow - fMinYShow) < 0.00001f) {
			fMinYShow -= 1;
			fMaxYShow += 1;
		}

		//正常情况5等分，放大后变成10等分
		int nStep =  5;
		double delta = (fMaxYShow - fMinYShow) * 10000 / nStep;
		int nD = (delta < 1.00001) ? 1 : int(delta + 0.99);
		if (nD > 25 && nD % 25 != 0)nD = (nD / 25 + 1) * 25;

		double fMin, fMax;
		int nn = 0;
		while (nn < 2)
		{
			if (nD <= 25)nD = 25;
			else if (nD <= 50)nD = 50;
			else if (nD <= 75)nD = 75;
			else if (nD <= 100)nD = 100;
			else if (nD <= 250)nD = 250;
			else if (nD <= 500)nD = 500;
			else if (nD <= 750)nD = 750;
			else if (nD <= 1000)nD = 1000;
			else if (nD <= 2500)nD = 2500;
			else if (nD <= 5000)nD = 5000;
			else if (nD <= 7500)nD = 7500;
			else if (nD <= 10000)nD = 10000;
			else nD = (nD / 10000 + 1) * 10000;

			delta = nD / 10000.0;
			fMin = (int)((fMinYShow + 0.00001) / delta) * delta;
			fMax = fMin + delta * nStep;
			if (fMax > fMaxYShow)
				break;
			//若上下越界，放宽一格Step
			nD++;
			nn++;
		}

		//居中处理
		int nGap = 1;
		while (fMax - delta * nGap > fMaxYShow)
		{
			nGap++;
		}
		fMaxYShow = fMax - delta * (nGap / 2);
		fMinYShow = fMin - delta * (nGap / 2);
	}

	void BondQuoteBrokerChart::GetShowLimit1(double fMinXShow, double fMaxXShow)
	{
		PriceKLineList* pDataKLine = GetPriceKLineList();
		if (pDataKLine->m_List.size() <= 0)
		{
			m_dMinY = min_show_def;
			m_dMaxY = max_show_def;
			return;
		}

		m_dMinY = 1000;
		m_dMaxY = -1000;

		bool bBegin = true;
		std::list<PriceKLine>::iterator it;
		std::list<PriceKLine>::iterator itLast1 = pDataKLine->m_List.end();
		std::list<PriceKLine>::iterator itLast2 = pDataKLine->m_List.end();
		int lastitinwork1 = -1;
		int lastitinwork2 = -1;
		int i = 0;
		bool bEnd1 = false;
		bool bEnd2 = false;
		for (it = pDataKLine->m_List.begin(); it != pDataKLine->m_List.end(); ++it, i++) {
			if (i < fMinXShow) {
				if (m_bShowLine1 && it->m_Price1 > QB_VALID_MIN_PRICE) {
					itLast1 = it;
					lastitinwork1 = i;
				}
				if (m_bShowLine2 && it->m_Price2 > QB_VALID_MIN_PRICE) {
					itLast2 = it;
					lastitinwork2 = i;
				}
			}
			else {
				if (i <= fMaxXShow) {
					if (m_bShowLine1 && it->m_Price1 > QB_VALID_MIN_PRICE) {
						if (itLast1 == pDataKLine->m_List.end()) {//first point
							m_dMinY = min(m_dMinY, it->m_Price1);
							m_dMaxY = max(m_dMaxY, it->m_Price1);
						}
						else {
							if (lastitinwork1 < fMinXShow) {
								double fp = (fMinXShow - lastitinwork1) * (it->m_Price1 - itLast1->m_Price1) / (i - lastitinwork1) + itLast1->m_Price1;
								//double fp = it->m_Price1 - (it->m_Price1 - itLast1->m_Price1) * (i - fMinXShow);
								m_dMinY = min(m_dMinY, fp);
								m_dMaxY = max(m_dMaxY, fp);
							}
							m_dMinY = min(m_dMinY, it->m_Price1);
							m_dMaxY = max(m_dMaxY, it->m_Price1);
						}
						itLast1 = it;
						lastitinwork1 = i;
					}
					if (m_bShowLine2 && it->m_Price2 > QB_VALID_MIN_PRICE) {
						if (itLast2 == pDataKLine->m_List.end()) {//first point
							m_dMinY = min(m_dMinY, it->m_Price2);
							m_dMaxY = max(m_dMaxY, it->m_Price2);
						}
						else {
							if (lastitinwork2 < fMinXShow) {
								double fp = (fMinXShow - lastitinwork2) * (it->m_Price2 - itLast2->m_Price2) / (i - lastitinwork2) + itLast2->m_Price2;
								m_dMinY = min(m_dMinY, fp);
								m_dMaxY = max(m_dMaxY, fp);
							}
							m_dMinY = min(m_dMinY, it->m_Price2);
							m_dMaxY = max(m_dMaxY, it->m_Price2);
						}
						itLast2 = it;
						lastitinwork2 = i;
					}
				}
				else {
					if (m_bShowLine1) {
						if (!bEnd1 && it->m_Price1 > QB_VALID_MIN_PRICE) {
							if (itLast1 == pDataKLine->m_List.end()) {
								m_dMinY = min(m_dMinY, it->m_Price1);
								m_dMaxY = max(m_dMaxY, it->m_Price1);
							}
							else {
								if (lastitinwork1 < fMinXShow) {
									double fp = (it->m_Price1 - itLast1->m_Price1) * (fMinXShow - lastitinwork1) / (i - lastitinwork1) + itLast1->m_Price1;
									m_dMinY = min(m_dMinY, fp);
									m_dMaxY = max(m_dMaxY, fp);
								}
								double fp = (it->m_Price1 - itLast1->m_Price1) * (fMaxXShow - lastitinwork1) / (i - lastitinwork1) + itLast1->m_Price1;
								m_dMinY = min(m_dMinY, fp);
								m_dMaxY = max(m_dMaxY, fp);
							}
							bEnd1 = true;
						}
					}
					else bEnd1 = true;
					if (m_bShowLine2) {
						if (!bEnd2 && it->m_Price2 > QB_VALID_MIN_PRICE) {
							if (itLast2 == pDataKLine->m_List.end()) {
								m_dMinY = min(m_dMinY, it->m_Price1);
								m_dMaxY = max(m_dMaxY, it->m_Price1);
							}
							else {
								if (lastitinwork2 < fMinXShow) {
									double fp = (it->m_Price2 - itLast2->m_Price2) * (fMinXShow - lastitinwork2) / (i - lastitinwork2) + itLast2->m_Price2;
									m_dMinY = min(m_dMinY, fp);
									m_dMaxY = max(m_dMaxY, fp);
								}
								double fp = (it->m_Price2 - itLast2->m_Price2) * (fMaxXShow - lastitinwork2) / (i - lastitinwork2) + itLast2->m_Price2;
								m_dMinY = min(m_dMinY, fp);
								m_dMaxY = max(m_dMaxY, fp);
							}
							bEnd2 = true;
						}
					}
					else bEnd2 = true;
					if (bEnd1 && bEnd2)break;
				}
			}
		}
		if (m_dMinY > 999 || m_dMaxY < QB_VALID_MIN_PRICE) {
			m_dMinY = min_show_def;
			m_dMaxY = max_show_def;
		}
	}

	void BondQuoteBrokerChart::GetShowLimit2(double fMinXShow, double fMaxXShow)
	{
		PriceRealTimeList* pRealTime = GetPriceRealTimeList();
		if (pRealTime->m_List.size() <= 0) {
			m_dMinY = min_show_def;
			m_dMaxY = max_show_def;
			return;
		}

		m_dMinY = 1000;
		m_dMaxY = -1000;

		bool bBegin = true;
		std::list<PriceRealTime>::iterator it;
		std::list<PriceRealTime>::iterator itLast1 = pRealTime->m_List.end();
		std::list<PriceRealTime>::iterator itLast2 = pRealTime->m_List.end();
		int lastitinwork1 = -1;
		int lastitinwork2 = -1;
		int i = 0;
		bool bEnd1 = false;
		bool bEnd2 = false;
		for (it = pRealTime->m_List.begin(); it != pRealTime->m_List.end(); it++, i++) {
			if (i < fMinXShow) {
				if (m_bShowLine1 && it->m_Price1 > -999) {
					itLast1 = it;
					lastitinwork1 = i;
				}
				if (m_bShowLine2 && it->m_Price2 > -999) {
					itLast2 = it;
					lastitinwork2 = i;
				}
			}
			else {
				if (i <= fMaxXShow) {
					if (m_bShowLine1 && it->m_Price1 > -999) {
						if (itLast1 == pRealTime->m_List.end()) {//first point
							m_dMinY = min(m_dMinY, it->m_Price1);
							m_dMaxY = max(m_dMaxY, it->m_Price1);
						}
						else {
							if (lastitinwork1 < fMinXShow) {
								double fp = it->m_Price1 - (it->m_Price1 - itLast1->m_Price1) * (i - fMinXShow);
								m_dMinY = min(m_dMinY, fp);
								m_dMaxY = max(m_dMaxY, fp);
							}
							m_dMinY = min(m_dMinY, it->m_Price1);
							m_dMaxY = max(m_dMaxY, it->m_Price1);
						}
						itLast1 = it;
						lastitinwork1 = i;
					}
					if (m_bShowLine2 && it->m_Price2 > -999) {
						if (itLast2 == pRealTime->m_List.end()) {//first point
							m_dMinY = min(m_dMinY, it->m_Price2);
							m_dMaxY = max(m_dMaxY, it->m_Price2);
						}
						else {
							if (lastitinwork2 < fMinXShow) {
								double fp = it->m_Price2 - (it->m_Price2 - itLast2->m_Price2) * (i - fMinXShow);
								m_dMinY = min(m_dMinY, fp);
								m_dMaxY = max(m_dMaxY, fp);
							}

							m_dMinY = min(m_dMinY, it->m_Price2);
							m_dMaxY = max(m_dMaxY, it->m_Price2);
						}
						itLast2 = it;
						lastitinwork2 = i;
					}
				}
				else {
					if (m_bShowLine1) {
						if (!bEnd1 && it->m_Price1 > -999) {
							if (itLast1 == pRealTime->m_List.end()) {
								m_dMinY = min(m_dMinY, it->m_Price1);
								m_dMaxY = max(m_dMaxY, it->m_Price1);
							}
							else {
								if (lastitinwork1 < fMinXShow) {
									double fp = (it->m_Price1 - itLast1->m_Price1) * (fMinXShow - lastitinwork1) / (i - lastitinwork1) + itLast1->m_Price1;
									m_dMinY = min(m_dMinY, fp);
									m_dMaxY = max(m_dMaxY, fp);
								}
								double fp = (it->m_Price1 - itLast1->m_Price1) * (fMaxXShow - lastitinwork1) / (i - lastitinwork1) + itLast1->m_Price1;
								m_dMinY = min(m_dMinY, fp);
								m_dMaxY = max(m_dMaxY, fp);
							}
							bEnd1 = true;
						}
					}
					else bEnd1 = true;
					if (m_bShowLine2) {
						if (!bEnd2 && it->m_Price2 > -999) {
							if (itLast2 == pRealTime->m_List.end()) {
								m_dMinY = min(m_dMinY, it->m_Price2);
								m_dMaxY = max(m_dMaxY, it->m_Price2);
							}
							else {
								if (lastitinwork2 < fMinXShow) {
									double fp = (it->m_Price2 - itLast2->m_Price2) * (fMinXShow - lastitinwork2) / (i - lastitinwork2) + itLast2->m_Price2;
									m_dMinY = min(m_dMinY, fp);
									m_dMaxY = max(m_dMaxY, fp);
								}
								double fp = (it->m_Price2 - itLast2->m_Price2) * (fMaxXShow - lastitinwork2) / (i - lastitinwork2) + itLast2->m_Price2;
								m_dMinY = min(m_dMinY, fp);
								m_dMaxY = max(m_dMaxY, fp);
							}
							bEnd2 = true;
						}
					}
					else bEnd2 = true;
					if (bEnd1 && bEnd2)break;
				}
			}
		}
	}

	int BondQuoteBrokerChart::GetKLineIndex(int nIndex, std::list<PriceKLine>::iterator& it) {
		PriceKLineList* pDataKLine = GetPriceKLineList();
		int i = 0;
		std::list<PriceKLine>::iterator itTemp = pDataKLine->m_List.end();
		for (it = pDataKLine->m_List.begin(); it != pDataKLine->m_List.end(); it++, i++) {
			if (i == nIndex) {
				return i;
			}
			itTemp = it;
		}
		if (it == pDataKLine->m_List.end() && itTemp != pDataKLine->m_List.end()) {
			it = itTemp;
			return pDataKLine->m_List.size() - 1;
		}
		return -1;
	}

	QString	BondQuoteBrokerChart::GetKLineDateString(std::list<PriceKLine>::iterator& it)
	{
		PriceKLineList* pDataKLine = GetPriceKLineList();
		QString szText;
		if (it != pDataKLine->m_List.end()) {
			szText = strInt2DateInTable(it->m_Date);
		}
		return szText;
	}

	QString	BondQuoteBrokerChart::strInt2DateInTable(UINT nDate)
	{
		return QString::asprintf("%04d-%02d-%02d", nDate / 10000, (nDate % 10000) / 100, nDate % 100);
	}

	void BondQuoteBrokerChart::CheckSelectMode(xQBMarketStreamList_c* pRealTime)
	{
		if (!pRealTime) return;
		if (pRealTime->m_List.size() <= 0) return;

		std::list<xQBMarketStreamUnit_c>::iterator it;
		bool bNeedChange = false;
		if (IsYeildModel()) {
			std::map<int, int > mapTemp;
			for (it = pRealTime->m_List.begin(); it != pRealTime->m_List.end(); ++it) {
				if (strlen(it->m_body1.m_bidQuotetype) > 0 && strlen(it->m_body1.m_bidprice) > 0) mapTemp[atoi(it->m_body1.m_bidQuotetype)] ++;
				if (strlen(it->m_body1.m_ofrQuotetype) > 0 && strlen(it->m_body1.m_ofrprice) > 0)  mapTemp[atoi(it->m_body1.m_ofrQuotetype)] ++;
			}
			if (mapTemp[1] > 0 && mapTemp[1] > mapTemp[3]) 
			{
				m_nPrcType = prc_Clean;
			}
		}
	}

	int BondQuoteBrokerChart::GetPriceRealTimeIndex(int nIndex, std::list<PriceRealTime>::iterator& it) 
	{
		PriceRealTimeList* pRealTime = GetPriceRealTimeList();
		if (pRealTime->m_List.size() <= 0)return -1;
		int i = 0;
		std::list<PriceRealTime>::iterator itTemp = pRealTime->m_List.end();
		for (it = pRealTime->m_List.begin(); it != pRealTime->m_List.end(); it++, i++) {
			if (i == nIndex) {
				return i;
			}
			itTemp = it;
		}
		if (it == pRealTime->m_List.end() && itTemp != pRealTime->m_List.end()) {
			it = itTemp;
			return pRealTime->m_List.size() - 1;
		}
		return -1;
	}

	QString	BondQuoteBrokerChart::GetPriceRealTimeString(std::list<PriceRealTime>::iterator& it) 
	{
		PriceRealTimeList* pRealTime = GetPriceRealTimeList();
		QString szText;
		if (it != pRealTime->m_List.end()) {
			qb::base::CTime T(it->m_time);
			szText = QString::asprintf("%02d:%02d", T.GetHour(), T.GetMinute());
		}
		return szText;
	}

	bool BondQuoteBrokerChart::AddTipsFormat(float fYeild, float fClean, bool bBid, QString& sTps)
	{
		if (fYeild < QB_VALID_DISPLAY_PRICE && fClean < QB_VALID_DISPLAY_PRICE) return false;
		QString strTemp, strYld("--"), strCln("--");
		if (fYeild > QB_VALID_DISPLAY_PRICE) {
			strTemp = QString("%1").arg(fYeild, 0, 'f', 4, '0');
			strYld = SSTools::PriceRemoveZero(strTemp);
		}
		if (fClean > QB_VALID_DISPLAY_PRICE) {
			strTemp = QString("%1").arg(fClean, 0, 'f', 4, '0');
			strCln = SSTools::PriceRemoveZero(strTemp);
		}

		sTps.append(QString("\n%1 %2 (收益率)\n      %3 (净价)").arg(bBid ? "Bid" : "Ofr").arg(strYld).arg(strCln));
		return true;
	}

	bool BondQuoteBrokerChart::NeedToFilter(void)
	{
		QB_CHECK_RETURN_BOOL1(mBondInfo);
		if (!mBondInfo->is_interest_bond())return false;
		return IsYeildModel() && m_bCheckFilter;
	}

	void BondQuoteBrokerChart::FilterAbnormalVal(void)
	{
		QB_CHECK_RETURN_VOID1(mBondInfo);

		m_quotesFiltered.Clear();

		//只有利率债有剔除异常值功能
		if (!mBondInfo->is_interest_bond())
		{
			return;
		}

		//笔数小于3笔的不做异常值剔除
		PriceRealTimeList* pData = GetPriceRealTimeList2();
		const std::list<PriceRealTime>& lstPriceRealTime = pData->m_List;
		if (lstPriceRealTime.size() <= QUOTES_NUMBER)
		{
			return;
		}

		//和前后两笔报价，都相差50个bp，为异常值。若是最后一笔，则只跟上一笔比较，超过50BP为异常值
		std::list<PriceRealTime>::const_iterator iter = lstPriceRealTime.begin();
		for (; iter != lstPriceRealTime.end(); ++iter)
		{
			PriceRealTime cur = *iter;
			if (cur.m_PriceYeild1 < QB_VALID_MIN_PRICE && cur.m_PriceYeild2 < QB_VALID_MIN_PRICE) {
				m_quotesFiltered.Insert(cur);
				continue;
			}
			int  nVerify1 = 0, nVerify2 = 0;
			bool bPrc1 = false, bPrc2 = false;
			for (int nL = -5; nL <= 5; nL++) {
				FilterOneVerify(iter, lstPriceRealTime, nL, bPrc1, bPrc2);
				if (bPrc1) nVerify1++;
				if (bPrc2) nVerify2++;
			}
			if (nVerify1 > QUOTES_NUMBER) {
				cur.m_Price1 = cur.m_PriceYeild1 = NULL_VALUE;
			}
			if (nVerify2 > QUOTES_NUMBER) {
				cur.m_Price2 = cur.m_PriceYeild2 = NULL_VALUE;
			}
			m_quotesFiltered.Insert(cur);
		}
	}

	void BondQuoteBrokerChart::FilterOneVerify(std::list<PriceRealTime>::const_iterator iter, const std::list<PriceRealTime>& lstPriceRealTime, int nPosition, bool& prc1, bool& prc2)
	{
		prc1 = FilterOneVerifySingle(iter, lstPriceRealTime, nPosition, true);
		prc2 = FilterOneVerifySingle(iter, lstPriceRealTime, nPosition, false);
	}

	bool BondQuoteBrokerChart::FilterOneVerifySingle(std::list<PriceRealTime>::const_iterator iter, const std::list<PriceRealTime>& lstPriceRealTime, int nPosition, bool bBid)
	{
		bool bReturn = false;
		std::list<PriceRealTime>::const_iterator iterCompare = iter;
		if (nPosition > QUOTES_NUMBER || nPosition < -QUOTES_NUMBER || iterCompare == lstPriceRealTime.end())  return false;

		if (nPosition == 0) {
			return false;
		}
		else if (nPosition > 0) {
			while (nPosition > 0) {
				iterCompare++;
				if (iterCompare == lstPriceRealTime.end()) return false;
				if (bBid && iterCompare->m_PriceYeild1 != NULL_VALUE) nPosition--;
				else if (!bBid && iterCompare->m_PriceYeild2 != NULL_VALUE) nPosition--;
			}
		}
		else {
			while (nPosition < 0) {
				if (iterCompare == lstPriceRealTime.begin()) return false;
				iterCompare--;
				if (iterCompare == lstPriceRealTime.end()) return false;
				if (bBid && iterCompare->m_PriceYeild1 != NULL_VALUE) nPosition++;
				else if (!bBid && iterCompare->m_PriceYeild2 != NULL_VALUE) nPosition++;
			}
		}
		if (bBid) return AbnormalVal(iterCompare->m_PriceYeild1, iter->m_PriceYeild1);
		else return AbnormalVal(iterCompare->m_PriceYeild2, iter->m_PriceYeild2);
	}

	bool BondQuoteBrokerChart::AbnormalVal(float curPrice, float price1)
	{
		if (curPrice == NULL_VALUE && price1 == NULL_VALUE) return false;
		else if ((curPrice == NULL_VALUE && price1 != NULL_VALUE) || (curPrice != NULL_VALUE && price1 == NULL_VALUE)) {
			return true;
		}

		//bp阀值，超过bp阀值则认为是异常数据
		const float BP = 0.05f;
		bool bfirstAbnormal = (FloatCompare(abs(price1 - curPrice) - BP) > 0);
		return bfirstAbnormal;
	}

	int BondQuoteBrokerChart::FloatCompare(double f) const
	{
		const double EPSINON = 1e-6;

		if ((f >= -EPSINON) && (f <= EPSINON))
		{
			return 0;
		}
		else if (f > EPSINON)
		{
			return 1;
		}
		else
		{
			return -1;
		}
	}

	double BondQuoteBrokerChart::getXPos_Value(double value)
	{
		QB_CHECK_RETURN_VALUE(bcde, 0);

		if (m_dOriginMaxX - m_dOriginMinX == 0) return 0;
		return (value - m_dOriginMinX) / (m_dOriginMaxX - m_dOriginMinX) * bcde->rect().width();
	}

	void BondQuoteBrokerChart::paintEvent(QPaintEvent* event)
	{
		QStylePainter painter(this);

		painter.fillRect(rect(), StyleMgr::instance().color("BasicPlate"));
	}

	void BondQuoteBrokerChart::leaveEvent(QEvent* event)
	{
		QB_CHECK_RETURN_VOID1(customPlot);

		customPlot->replot();
	}

	void BondQuoteBrokerChart::onMouseMove(QMouseEvent* event)
	{
		QB_CHECK_RETURN_VOID1(customPlot && customPlot->graph(0) && customPlot->graph(1));

		double dm = customPlot->graph(0)->selectTest(event->pos(), false);
		double dmFirst = customPlot->graph(1)->selectTest(event->pos(), false);
		
		if ((0 < dm && 2 > dm) && (0 < dmFirst && 2 > dmFirst))
		{
			QPen pen(QColor(225, 146, 0));
			pen.setWidth(2);
			customPlot->graph(0)->setPen(pen);

			QPen penFirst(QColor(215, 31, 23));
			penFirst.setWidth(2);
			customPlot->graph(1)->setPen(penFirst);
		}
		else if (0 < dm && 2 > dm)
		{
			QPen pen(QColor(225, 146, 0));
			pen.setWidth(2);
			customPlot->graph(0)->setPen(pen);

			QColor color(QColor(195, 29, 22));
			color.setAlpha(100);
			QPen penFirst(color);
			penFirst.setWidth(1);
			customPlot->graph(1)->setPen(penFirst);
		}
		else if (0 < dmFirst && 2 > dmFirst)
		{
			QPen penFirst(QColor(215, 31, 23));
			penFirst.setWidth(2);
			customPlot->graph(1)->setPen(penFirst);

			QColor color(QColor(205, 119, 4));
			color.setAlpha(100);
			QPen pen(color);
			pen.setWidth(1);
			customPlot->graph(0)->setPen(pen);
		}
		else
		{
			QPen pen(QColor(205, 119, 4));
			pen.setWidth(1);
			customPlot->graph(0)->setPen(pen);
		
			QPen penFirst(QColor(195, 29, 22));
			penFirst.setWidth(1);
			customPlot->graph(1)->setPen(penFirst);
		}

		customPlot->replot();

		int i = 0;
		std::list<PriceRealTime>::iterator it;
		PriceRealTimeList* pRealTime = GetPriceRealTimeList();
		QString szTip, szTemp, szPrice;
		for (it = pRealTime->m_List.begin(); it != pRealTime->m_List.end(); ++it, i++) {
			int X = customPlot->xAxis->coordToPixel(i);
			if (abs(event->pos().x() - X) < 3) {
				if (m_nHotPoint != i) {
					m_nHotPoint = i;
				}

				qb::base::CTime T(it->m_time);
				szTip = QString::asprintf("%d-%02d-%02d %02d:%02d:%02d", T.GetYear(), T.GetMonth(), T.GetDay(), T.GetHour(), T.GetMinute(), T.GetSecond());
				int nLine = 0;
				if (getMarketType() == em_Exchange) {
					if (it->m_Price1 > QB_VALID_MIN_PRICE) {
						szPrice = QString("\nBid %1").arg(it->m_Price1, 0, 'f', 4, '0');
						szTip.append(szPrice);
						szTemp = SSTools::PriceRemoveZero(szPrice);
						szTip += szTemp;

						nLine++;
					}
					if (it->m_Price2 > QB_VALID_MIN_PRICE) {
						szPrice = QString("\nOfr %1").arg(it->m_Price2, 0, 'f', 4, '0');
						szTip.append(szPrice);
						szTemp = SSTools::PriceRemoveZero(szPrice);
						szTip += szTemp;

						nLine++;
					}

					if (szTip.length() > 0) {
						std::vector<QBTipCustomItem> vctCustomItem;
						QBTipCustomItem item;
						item.clrNormal = QColor(223, 174, 76, 255);
						item.col = 1;

						for (i = 0; i < nLine; i++)
						{
							item.row = i + 1;
							vctCustomItem.push_back(item);
						}

						QBTips::showText(event->screenPos().toPoint(), szTip, this, &vctCustomItem);
					}
				}
				else {
					if (IsYeildModel() && it->m_PriceYeild1 < -990 && it->m_PriceYeild2 < -990)
					{
						szTip.clear();
					}
					else if (!IsYeildModel() && it->m_PriceClean1 < -990 && it->m_PriceClean2 < -990)
					{
						szTip.clear();
					}
					else
					{
						if (AddTipsFormat(it->m_PriceYeild1, it->m_PriceClean1, true, szTip)) nLine += 2;
						if (AddTipsFormat(it->m_PriceYeild2, it->m_PriceClean2, false, szTip)) nLine += 2;
					}
				}
				if (szTip.length() > 0) {
					std::vector<QBTipCustomItem> vctCustomItem;
					QBTipCustomItem item;
					item.clrNormal = QColor(223, 174, 76, 255);
					
					for (i = 0; i < nLine; i++)
					{
						if (i % 2)
						{
							item.col = 6;
							item.row = i + 1;
							vctCustomItem.push_back(item);

							item.col = 7;
							item.row = i + 1;
							vctCustomItem.push_back(item);
						}
						else
						{
							item.col = 1;
							item.row = i + 1;
							vctCustomItem.push_back(item);

							item.col = 2;
							item.row = i + 1;
							vctCustomItem.push_back(item);
						}
					}

					QBTips::showText(event->screenPos().toPoint(), szTip, this, &vctCustomItem);
				}

				return;
			}
		}

		if (m_nHotPoint >= 0) {
			m_nHotPoint = -1;
			QBTips::hideTip();
		}
	}
}
